#pragma once

#include "lines.hpp"
#include "mockif.hpp"
#include "params.hpp"
#include "schrodinger.hpp"
#include "swing.hpp"
#include "types.h"
#include <cstdint>
#include <memory>
#include <vector>

namespace Trade {

enum PET_ACTION : uint32_t
{
  PET_NONE = 0,
  JERRY_RUN = 1,
  JERRY_STOP = 2,
  TOM_RUN = 4,
  TOM_STOP = 8,
  MICKEY_RUN = 16,
  MICKEY_STOP = 32,
  SET_CLOSE_PART = 64,
  SET_CLOSE_ALL = 128,
  CLOSE_PART = 256,
  CLOSE_ALL = 512,
  SET_OPEN_PRICE = 1024,
  OPENED = 2048,
  SET_CANCEL_CLOSE = 4096,
  CANCEL_CLOSE = 8192,
};

struct TheDeal
{
  char dir = 0;
  uint8_t to_amount = 0;
  uint8_t reached = 0;
  uint8_t c_case = 0;
  int index = 0;
  int64_t o_idx = 0;
  int64_t c_idx = 0;
  time_t o_tm = 0;
  time_t c_tm = 0;
  price_t o_hp = 0;
  price_t c_hp = 0;
  price_t rvn_max = 0;
  price_t hp_rvn_max = 0;
  int64_t idx_rvn_max = 0;
  price_t rvn_min = 0;
  price_t hp_rvn_min = 0;
  int64_t idx_rvn_min = 0;
  price_t rvn_px = 0;
  price_t rvn_hp = 0;
  price_t hpx_avg = 0;
};
using TheDealPtr = std::shared_ptr<TheDeal>;
using TheDealVector = std::vector<TheDealPtr>;

enum class DEAL_ACTION : uint8_t
{
  DA_NONE = 0,
  DA_RUN_TOM = 1,
  DA_RUN_JERRY = 2,
  DA_RUN_MICKEY = 3,
  DA_STOP_TOM = 4,
  DA_STOP_JERRY = 5,
  DA_STOP_MIKEY = 6,
  DA_CLOSE_POSITION = 7,
  DA_OPEN_POSITION = 8,
  DA_EMPTY_POSITION = 9,
};

#define TJM_SOLVED_SPP_DIR_WITH_TIMER 4 // case number

#define MAX_OPENING_LOSS_LINE_1 35
#define MAX_OPENING_LOSS_LINE_2 55

const case_t PRICE_CNO_GOOD = 1;
const case_t PRICE_CNO_GOOD_BUT_SWINT = 32;
const case_t PRICE_CNO_BAD = 64;

const price_t CLOSE_REVENUE_FOOL_FORCED = -80;
const price_t CLOSE_REVENUE_LOSS_ONE_THIRD = -25;
const price_t CLOSE_REVENUE_TOLERATE_REVENUE = -10;
const price_t CLOSE_REVENUE_WIN_ONE_THIRD = 25;
const price_t CLOSE_REVENUE_MIN_TO_WIN = 8;
const price_t MINIMAL_LOSS_RATE = .5;
const price_t MINIMAL_GOOD_TO_WIN_AMP = 30;
const price_t GOOD_TO_WIN_RATE = .8;

const price_t RVN_TWO_HUNDRED = 200.0 / 12;

// do NOT change the value below ____________________________
const case_t CASE_TOM_REACHED_SAMESIDE_LINE2 = 30001;
const case_t CASE_TOM_REACHED_SAMESIDE_LINE1 = 30002;
const case_t CASE_JERRY_REACHED_SAMESIDE_LINE2 = 30003;
const case_t CASE_JERRY_REACHED_SAMESIDE_LINE1 = 30004;
// Do not change the value above ----------------------------

const case_t CASE_TOM_REACHED_OPPOSITE_LINE = 30005;
const case_t CASE_JERRY_REACHED_OPPOSITE_LINE = 30006;

const case_t CASE_BIG_NEEDLE_OCCURED = 30021;
const case_t CASE_BIG_REVERSE_OCCURED = 30031;

const case_t CASE_REACHED_OPEN_BUT_OPPOSITE = 20001;

const case_t CASE_MIN_REVERSE_CASE_NO = 70000;
const case_t CASE_MAX_REVERSE_CASE_NO = 79999;
const case_t CASE_MIN_STOP_CASE_NO = 60000;
const case_t CASE_MAX_STOP_CASE_NO = 69999;
const case_t CASE_STOP_TOM_BY_STOP_WIN = 50001;

const case_t CASE_OPEN_AGAINST_BG_ZZZZ_1 = 10001;
const case_t CASE_JERRY_RUN_AGAINST_BD = 40001;

enum TJM_CASE_TYPE : case_t
{
  TJM_CASE_RUSH_NO_CONSISTENT = 0x0001,
  TJM_CASE_RUSH_CONSISTENT = 0x0002,
};

enum class TJM_PART_OF_A_WHOLE : uint8_t
{
  FRACTION_NONE_1 = 1,
  FRACTION_1_2 = 2,
  FRACTION_1_3 = 3,
  FRACTION_1_4 = 4,
  FRACTION_1_5 = 5,
  FRACTION_1_6 = 6,
  FRACTION_1_7 = 7,
  FRACTION_1_8 = 8,
  FRACTION_1_9 = 9,
  FRACTION_1_10 = 10,
  FRACTION_1_16 = 16,
};

struct TJM
{
  uint8_t r_tom = 0;
  uint8_t r_jerry = 0;
  uint8_t r_mickey = 0;
  uint8_t r_swing = 0;

  bool reached_l2_fo = false;
  uint8_t jerry_reached = 0;
  bool b_marked_up = false;
  bool b_marked_dn = false;

  int total_unit = 8;
  int vol_per_unit = 16;
  int time_reverse_case = 0;

  int deal_times = 0;
  int jerry_to_amount = 0;

  int whole_position = 0;

  case_t ft_up = 0;
  case_t ft_dn = 0;
  case_t tjm_cno = 0;

  price_t hp_now_ = 0;
  price_t revenue = 0;
  time_t t_now_ = 0;
  int64_t idx__ = 0;
  time_t tm_big_needle = 0;
  time_t tm_big_reverse = 0;

  time_t t_mickey_t1 = 0;
  time_t t_mickey_t2 = 0;

  Params* vo = nullptr;

  TheDealVector tdv_tom;
  TheDealVector tdv_mik;
  Schrodinger tom;
  Schrodinger jerry;
  Schrodinger mickey;
  Swing swing;

  MockIF mockif;

  TJM() { tom.set_mock_nterface(&mockif); }

  void set_data(const Params* vo, const price_t hpx) noexcept
  {
    this->vo = (Params*)(vo);
    idx__ = vo->idx__;
    t_now_ = vo->t_now_;
    hp_now_ = hpx;
  }

  void print() const noexcept;
  int reached(event_t& event, Lines& lines) noexcept;
  int lines_changed_() noexcept;

  bool good_entry_price(const dir_t d,
                        price_t& hp_base,
                        case_t& case_no) noexcept;
  bool should_reache_line2(const dir_t d) noexcept;
  bool explicit_direction_and_timer_out(const dir_t d) noexcept;

  // Do Actions
  int action_jerry_run(const dir_t d,
                       const Lines& l,
                       const case_t cno) noexcept;
  int action_jerry_stop(const case_t cno) noexcept;
  int action_tom_run(const dir_t d,
                     const price_t hpx,
                     const Lines& l,
                     const case_t cno) noexcept;
  int action_tom_stop(const price_t hpx, const case_t cno) noexcept;
  int action_mickey_run(const dir_t d,
                        const price_t hpx,
                        const Lines& l,
                        const case_t cno) noexcept;
  int action_mickey_stop(const case_t cno) noexcept;

  bool pricing_close_min_to_win(price_t& revenue) const noexcept;
  bool pricing_close_stop_loss(price_t& revenue) const noexcept;
  price_t priceing_close_good_to_win() const noexcept;

  bool reached_jerry_time_out() const noexcept;

  int jerry_reached_line2(const event_t event) noexcept;
  // bool reached_line2_and_force_opposite() noexcept;

  void print_revenue() noexcept;

  void reset() noexcept;

  price_t finish() noexcept;

  int send_pet_timer(const dir_t d,
                     const int8_t type,
                     const time_t t1 = 0,
                     const time_t t2 = 0,
                     const time_t tm = 0,
                     const price_t hpx = 0,
                     const int8_t renew = 0) const noexcept;
  void send_timer() const noexcept;

  bool vision_dir_opposite_to_tom() const noexcept;
  bool vision_dir_sameside_to_tom() const noexcept;
  int print_all_deal(FILE* fp) noexcept;
  event_t fix_dir_tjm() noexcept;

  int td_show_revunue() const noexcept;

  void set_whole_position(const int vol) noexcept;
  int get_part_volume(const TJM_PART_OF_A_WHOLE part) const noexcept;

  void retrive_mickey_timer(const dir_t d,
                            time_t& tm1,
                            time_t& tm2) const noexcept;

  const Lines* get_lines(const dir_t d) noexcept;
  const dir_t get_cur_trade_dir() const noexcept;
};

} // namespace Trade

extern void
do_something();
